import 'reflect-metadata';

/**
 * 直接在类，方法上定义元数据
 */

/**
 * 等同于 > Reflect.defineMetadata('decribe-class', '都是地球人', People);
 */
@Reflect.metadata('decribe-class', '都是地球人')
class People {
  @Reflect.metadata('decribe-attr', '不为空名字')
  username = 'wangwu';
  @Reflect.metadata('decribe-age', 'age')
  age: number = 22;
  @Reflect.metadata('decribe-fn', '去三里屯吃？')
  eat() {}
  @Reflect.metadata('decribe-fn2', '去三里屯吃？ 2')
  eat2() {}

  // @Reflect.metadata('decribe-fn3', 'eat3')
  eat3() {}
}

/**
 * 获取
 */
console.log(Reflect.getMetadata('decribe-class', People));
/**
 * 属性与方法，均在原型上获取
 * Reflect.defineMetadata('decribe-fn', '去三里屯吃？', People.prototype);
 */
console.log(Reflect.getMetadata('decribe-attr', People.prototype, 'username'));
console.log(Reflect.getMetadata('decribe-fn', People.prototype, 'eat'));

class ChinesePeople extends People {
  // @Reflect.metadata('ChinesePeople-constructor', 'ChinesePeople_constructor')
  constructor(name: string) {
    super();
  }
  @Reflect.metadata('decribe-ChinesePeople-fn1', '去三里屯吃？ C1')
  @Reflect.metadata('decribe-ChinesePeople-fn2', '去三里屯吃？ C2')
  eat2() {}
}

// 都是地球人
console.log('decribe-class >>> 1', Reflect.getMetadata('decribe-class', ChinesePeople));

// 去三里屯吃？ | 子类获取父类原型上方法
console.log('decribe-fn >>> 2', Reflect.getMetadata('decribe-fn', ChinesePeople.prototype, 'eat'));

// 不为空名字
console.log(
  '3 decribe-attr >>> ',
  Reflect.getMetadata('decribe-attr', ChinesePeople.prototype, 'username')
);

// ✔
if (Reflect.hasMetadata('decribe-fn', ChinesePeople.prototype, 'eat')) {
  console.log(
    '4 hasMetadata => ChinesePeople 原型上通过继承也获取到了eat方法和eat方法上挂载的元数据'
  );
}
// ✔
if (Reflect.hasOwnMetadata('decribe-fn2', People.prototype, 'eat2')) {
  console.log('5 只获取自身的 不会通过原型链往上查找');
}
// ✘
if (Reflect.hasOwnMetadata('decribe-fn', ChinesePeople.prototype, 'eat')) {
  console.log('6 只获取自身的 不会通过原型链往上查找');
}

/**
 * getOwnMetadata 与 hasOwnMetadata只会从自身查找，原型上的不管，与getMetadata 和 hasMetadata 不同，他俩都会往原型上查找。
 */

console.log(Reflect.getMetadataKeys(People)); // ['decribe-class']
console.log(Reflect.getMetadataKeys(People.prototype)); // []
console.log(Reflect.getMetadataKeys(ChinesePeople)); // ['decribe-class']
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype)); // []

Reflect.getMetadataKeys(People.prototype, 'username').forEach(v => {
  console.log('username 111', Reflect.getMetadata(v, People.prototype, 'username')); // [Function: Object]
});

// 不定义类型 发现获取到的属性的类型 -> design:type 均为 [Function: Object]
// 定义好了，类型便是正确的
Reflect.getMetadataKeys(People.prototype, 'age').forEach(v => {
  console.log('age 111', Reflect.getMetadata(v, People.prototype, 'age')); // [Function: Number]
});
/**
 * 在 People.prototype.eat3 这个方法的身上，当我们没有使用Reflect装饰器去定义元数据时，返回的元数据为[]，默认的元数据类型也不会存在
 */
console.log(333, Reflect.getMetadataKeys(People.prototype, 'eat3'));

/*
  [
    'design:returntype',
    'design:paramtypes',
    'design:type',
    'decribe-fn'
  ]
*/
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype, 'eat'));

/*
  [
    'design:returntype',
    'design:paramtypes',
    'design:type',
    'decribe-ChinesePeople-fn2',
    'decribe-ChinesePeople-fn1',
    'decribe-fn2'
  ]

  会读继承类上的方法的元数据与当前方法身上的元数据
*/
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype, 'eat2')); // []

/*
  只读取自身的

  [
    'design:returntype',
    'design:paramtypes',
    'design:type',
    'decribe-ChinesePeople-fn2',
    'decribe-ChinesePeople-fn1'
  ]
*/
console.log(Reflect.getOwnMetadataKeys(ChinesePeople.prototype, 'eat2')); // []

// design 开头的都是默认带的

export {};
